"""
景点视图
"""
import traceback
import time
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticatedOrReadOnly, IsAuthenticated, IsAdminUser
from rest_framework.pagination import PageNumberPagination
from django.db.models import Q
from django.core.cache import cache
from django.conf import settings
from .models import Attraction, AttractionImage, AttractionReview, AttractionFavorite
from .serializers import AttractionSerializer, AttractionImageSerializer, AttractionReviewSerializer
from .recommendations import RecommendationEngine
from apps.utils.monitoring import performance_monitor, error_collector
from utils.image_utils import validate_image, compress_image
from utils.amap_client import AMapClient


class CustomPageNumberPagination(PageNumberPagination):
    """自定义分页类，确保page_size参数能正确工作"""
    page_size = 12  # 默认每页12个
    page_size_query_param = 'page_size'
    max_page_size = 100


class AttractionViewSet(viewsets.ModelViewSet):
    """景点视图集"""
    queryset = Attraction.objects.all()
    serializer_class = AttractionSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    pagination_class = CustomPageNumberPagination
    
    def get_queryset(self):
        """优化查询，使用select_related和prefetch_related减少数据库查询"""
        if hasattr(self, '_queryset') and self._queryset is not None:
            return self._queryset
        queryset = super().get_queryset()
        
        # 如果是列表查询，优化收藏关系查询（解决N+1问题）
        if self.action == 'list' and self.request.user.is_authenticated:
            from django.db.models import Prefetch, Count
            from .models import AttractionFavorite
            # 预加载当前用户的收藏关系
            queryset = queryset.prefetch_related(
                Prefetch(
                    'favorited_by',
                    queryset=AttractionFavorite.objects.filter(user=self.request.user),
                    to_attr='user_favorites'
                )
            )
            # 使用annotate优化收藏数查询
            queryset = queryset.annotate(favorites_count_annotated=Count('favorited_by'))
            # 使用annotate优化图片数和评价数查询
            from .models import AttractionImage, AttractionReview
            queryset = queryset.annotate(
                images_count_annotated=Count('images', distinct=True),
                reviews_count_annotated=Count('reviews', distinct=True)
            )
        
        # 确保查询集有默认排序（避免分页警告）
        if not queryset.query.order_by:
            queryset = queryset.order_by('-popularity', 'name', 'id')
        
        return queryset
    
    def get_serializer_context(self):
        """添加request到序列化器context，用于生成完整URL"""
        context = super().get_serializer_context()
        context['request'] = self.request
        return context
    
    def list(self, request, *args, **kwargs):
        """列表查询，优化查询性能"""
        # 获取优化后的查询集
        queryset = self.get_queryset()
        # 使用only()只获取需要的字段，减少数据传输
        # 注意：primary_image是序列化器方法字段，不是模型字段，不能包含在only()中
        queryset = queryset.only(
            'id', 'name', 'category', 'ticket_price', 'rating', 
            'popularity', 'address', 'longitude', 'latitude', 'amap_image_urls'
        )
        # 确保排序
        if not queryset.query.order_by:
            queryset = queryset.order_by('-popularity', 'name', 'id')
        # 设置临时queryset，避免get_queryset被重复调用
        self._queryset = queryset
        try:
            response = super().list(request, *args, **kwargs)
        finally:
            # 清理临时queryset
            if hasattr(self, '_queryset'):
                delattr(self, '_queryset')
        return response
    
    def retrieve(self, request, *args, **kwargs):
        """详情查询，使用缓存优化"""
        pk = kwargs.get('pk')
        cache_key = f'attraction_detail_{pk}'
        
        # 尝试从缓存获取
        cached_data = cache.get(cache_key)
        if cached_data:
            # 构建完整URL
            if cached_data.get('primary_image') and not cached_data['primary_image'].startswith('http'):
                cached_data['primary_image'] = request.build_absolute_uri(cached_data['primary_image'])
            return Response(cached_data)
        
        # 获取数据
        response = super().retrieve(request, *args, **kwargs)
        
        # 转换为相对路径后缓存
        cache_data = response.data.copy()
        if cache_data.get('primary_image'):
            primary_image = cache_data['primary_image']
            if primary_image.startswith('http'):
                if '/media/' in primary_image:
                    cache_data['primary_image'] = '/media/' + primary_image.split('/media/')[-1]
                elif primary_image.startswith(request.build_absolute_uri('/')):
                    cache_data['primary_image'] = primary_image.replace(request.build_absolute_uri('/'), '/')
        
        # 缓存数据（2小时）
        cache.set(cache_key, cache_data, timeout=7200)
        
        return response
    
    def get_permissions(self):
        """根据action返回不同的权限"""
        # CRUD操作需要管理员权限
        if self.action in ['create', 'update', 'partial_update', 'destroy']:
            return [IsAdminUser()]
        # 图片上传和删除需要管理员权限
        elif self.action in ['upload_image', 'delete_image']:
            return [IsAdminUser()]
        # 其他操作（list, retrieve, search等）允许所有人访问
        return super().get_permissions()
    
    @action(detail=False, methods=['get'])
    def search(self, request):
        """搜索景点"""
        keyword = request.query_params.get('keyword', '')
        category = request.query_params.get('category', '')
        min_price = request.query_params.get('min_price')
        max_price = request.query_params.get('max_price')
        
        queryset = self.get_queryset()
        
        if keyword:
            queryset = queryset.filter(
                Q(name__icontains=keyword) | Q(description__icontains=keyword)
            )
        
        if category:
            queryset = queryset.filter(category=category)
        
        if min_price:
            queryset = queryset.filter(ticket_price__gte=min_price)
        
        if max_price:
            queryset = queryset.filter(ticket_price__lte=max_price)
        
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def rankings(self, request):
        """景点排行榜"""
        from django.db.models import Count, Avg
        from django.utils import timezone
        from datetime import timedelta
        
        ranking_type = request.query_params.get('type', 'hot')  # hot, rating, favorite
        category = request.query_params.get('category', '')
        period = request.query_params.get('period', 'all')  # today, week, month, all
        limit = int(request.query_params.get('limit', 20))
        
        queryset = self.get_queryset()
        
        # 分类筛选
        if category:
            queryset = queryset.filter(category=category)
        
        # 时间范围筛选（用于浏览量统计）
        if period != 'all':
            now = timezone.now()
            if period == 'today':
                start_date = now.replace(hour=0, minute=0, second=0, microsecond=0)
            elif period == 'week':
                start_date = now - timedelta(days=7)
            elif period == 'month':
                start_date = now - timedelta(days=30)
            else:
                start_date = None
            
            # 注意：这里假设有views字段，如果没有可以忽略时间筛选
            # queryset = queryset.filter(views_updated_at__gte=start_date)
        
        # 根据类型排序
        if ranking_type == 'hot':
            # 热门排行榜（按浏览量，如果没有views字段则按创建时间）
            queryset = queryset.order_by('-created_at')[:limit]
        elif ranking_type == 'rating':
            # 评分排行榜
            queryset = queryset.annotate(
                avg_rating=Avg('reviews__rating')
            ).filter(avg_rating__isnull=False).order_by('-avg_rating', '-created_at')[:limit]
        elif ranking_type == 'favorite':
            # 收藏排行榜
            queryset = queryset.annotate(
                favorite_count=Count('favorites')
            ).filter(favorite_count__gt=0).order_by('-favorite_count', '-created_at')[:limit]
        elif ranking_type == 'newest':
            # 最新添加
            queryset = queryset.order_by('-created_at')[:limit]
        else:
            queryset = queryset.order_by('-created_at')[:limit]
        
        serializer = self.get_serializer(queryset, many=True)
        return Response({
            'type': ranking_type,
            'category': category,
            'period': period,
            'count': len(serializer.data),
            'results': serializer.data
        })
    
    @action(detail=True, methods=['get', 'post'])
    def reviews(self, request, pk=None):
        """获取或创建景点评价"""
        attraction = self.get_object()
        
        if request.method == 'GET':
            # 获取评价列表（分页）
            # 函数说明：支持 page 与 page_size 参数，返回标准分页结构
            reviews_qs = attraction.reviews.all().order_by('-created_at')
            paginator = PageNumberPagination()
            try:
                page_size = int(request.query_params.get('page_size', 20))
            except Exception:
                page_size = 20
            paginator.page_size = page_size
            page = paginator.paginate_queryset(reviews_qs, request, view=self)
            serializer = AttractionReviewSerializer(page, many=True)
            return paginator.get_paginated_response(serializer.data)
        
        elif request.method == 'POST':
            # 创建评价（需要登录）
            if not request.user.is_authenticated:
                return Response(
                    {'error': '请先登录'},
                    status=status.HTTP_401_UNAUTHORIZED
                )
            
            # 检查是否已经评价过
            existing_review = AttractionReview.objects.filter(
                attraction=attraction,
                user=request.user
            ).first()
            
            if existing_review:
                return Response(
                    {'error': '您已经评价过该景点'},
                    status=status.HTTP_400_BAD_REQUEST
                )
            
            # 创建评价
            serializer = AttractionReviewSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save(attraction=attraction, user=request.user)
                
                # 更新景点评分
                from django.db.models import Avg
                avg_rating = attraction.reviews.aggregate(avg=Avg('rating'))['avg']
                if avg_rating:
                    attraction.rating = round(avg_rating, 2)
                    attraction.save()
                
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    @action(detail=True, methods=['get'])
    def images(self, request, pk=None):
        """获取景点图片列表（优先返回高德地图图片，支持分页）"""
        attraction = self.get_object()
        
        # 优先使用高德地图图片
        amap_images = []
        if attraction.amap_image_urls and len(attraction.amap_image_urls) > 0:
            # 使用已保存的高德地图图片URL
            amap_images = [
                {
                    'id': f'amap_{i}',
                    'image': url,
                    'url': url,
                    'is_primary': i == 0,
                    'source': 'amap'
                }
                for i, url in enumerate(attraction.amap_image_urls)
            ]
        elif attraction.amap_poi_id:
            # 如果有POI ID但没有图片URL，尝试从API获取
            try:
                amap_client = AMapClient()
                poi_detail = amap_client.get_poi_detail(attraction.amap_poi_id)
                
                if poi_detail.get('status') == '1' and poi_detail.get('pois'):
                    poi = poi_detail['pois'][0]
                    photos = poi.get('photos', [])
                    
                    image_urls = []
                    for photo in photos:
                        if isinstance(photo, dict) and photo.get('url'):
                            image_urls.append(photo['url'])
                        elif isinstance(photo, str):
                            image_urls.append(photo)
                    
                    if image_urls:
                        # 保存到数据库
                        attraction.amap_image_urls = image_urls
                        attraction.save(update_fields=['amap_image_urls'])
                        
                        amap_images = [
                            {
                                'id': f'amap_{i}',
                                'image': url,
                                'url': url,
                                'is_primary': i == 0,
                                'source': 'amap'
                            }
                            for i, url in enumerate(image_urls)
                        ]
            except Exception as e:
                import logging
                logger = logging.getLogger(__name__)
                logger.warning(f'从高德地图API获取图片失败: {str(e)}')
        
        # 如果有高德地图图片，分页返回
        if amap_images:
            paginator = PageNumberPagination()
            try:
                page_size = int(request.query_params.get('page_size', 50))
            except Exception:
                page_size = 50
            paginator.page_size = page_size
            page = paginator.paginate_queryset(amap_images, request, view=self)
            return paginator.get_paginated_response(page)
        
        # 否则返回本地图片（分页）
        images_qs = attraction.images.all().order_by('-created_at')
        paginator = PageNumberPagination()
        try:
            page_size = int(request.query_params.get('page_size', 50))
        except Exception:
            page_size = 50
        paginator.page_size = page_size
        page = paginator.paginate_queryset(images_qs, request, view=self)
        serializer = AttractionImageSerializer(page, many=True, context={'request': request})
        return paginator.get_paginated_response(serializer.data)
    
    @action(detail=True, methods=['post'], permission_classes=[IsAdminUser])
    def upload_image(self, request, pk=None):
        """上传景点图片"""
        attraction = self.get_object()
        
        if 'image' not in request.FILES:
            return Response(
                {'error': '请选择要上传的图片'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        image_file = request.FILES['image']
        
        # 验证图片
        is_valid, error_message = validate_image(image_file)
        if not is_valid:
            return Response(
                {'error': error_message},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        try:
            # 压缩图片
            compressed_image = compress_image(image_file)
            
            # 检查是否设置为主图
            is_primary = request.data.get('is_primary', False)
            
            # 如果设置为主图，取消其他主图
            if is_primary:
                AttractionImage.objects.filter(attraction=attraction, is_primary=True).update(is_primary=False)
            
            # 创建图片记录
            image_obj = AttractionImage.objects.create(
                attraction=attraction,
                image=compressed_image,
                is_primary=is_primary
            )
            
            serializer = AttractionImageSerializer(image_obj, context={'request': request})
            return Response(serializer.data, status=status.HTTP_201_CREATED)
            
        except Exception as e:
            return Response(
                {'error': f'图片上传失败：{str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    
    @action(detail=True, methods=['patch'], permission_classes=[IsAdminUser])
    def set_primary_image(self, request, pk=None):
        """设置景点主图"""
        image_id = request.data.get('image_id')
        if not image_id:
            return Response(
                {'error': '请提供图片ID'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        try:
            attraction = self.get_object()
            # 取消其他主图
            AttractionImage.objects.filter(attraction=attraction, is_primary=True).update(is_primary=False)
            # 设置新的主图
            image_obj = AttractionImage.objects.get(id=image_id, attraction_id=pk)
            image_obj.is_primary = True
            image_obj.save()
            
            serializer = AttractionImageSerializer(image_obj, context={'request': request})
            return Response(serializer.data, status=status.HTTP_200_OK)
        except AttractionImage.DoesNotExist:
            return Response(
                {'error': '图片不存在'},
                status=status.HTTP_404_NOT_FOUND
            )
        except Exception as e:
            return Response(
                {'error': f'设置主图失败：{str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    
    @action(detail=True, methods=['delete'], permission_classes=[IsAdminUser])
    def delete_image(self, request, pk=None):
        """删除景点图片"""
        image_id = request.query_params.get('image_id')
        if not image_id:
            return Response(
                {'error': '请提供图片ID'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        try:
            image_obj = AttractionImage.objects.get(id=image_id, attraction_id=pk)
            image_obj.delete()
            return Response({'message': '图片删除成功'}, status=status.HTTP_200_OK)
        except AttractionImage.DoesNotExist:
            return Response(
                {'error': '图片不存在'},
                status=status.HTTP_404_NOT_FOUND
            )
    
    @action(detail=False, methods=['get'], permission_classes=[IsAdminUser])
    def statistics(self, request):
        """获取景点统计信息（管理员）"""
        from django.db.models import Count, Q
        from django.utils import timezone
        from datetime import timedelta
        
        total_attractions = Attraction.objects.count()
        
        # 按分类统计
        category_stats = Attraction.objects.values('category').annotate(
            count=Count('id')
        ).order_by('-count')
        
        # 有图片的景点数
        with_images = Attraction.objects.filter(
            Q(amap_image_urls__isnull=False) & ~Q(amap_image_urls=[])
        ).count()
        
        # 有评价的景点数
        with_reviews = Attraction.objects.filter(
            reviews__isnull=False
        ).distinct().count()
        
        # 最近7天新增
        seven_days_ago = timezone.now() - timedelta(days=7)
        recent_attractions = Attraction.objects.filter(
            created_at__gte=seven_days_ago
        ).count()
        
        # 热门景点（按收藏数）
        popular_attractions = Attraction.objects.annotate(
            favorite_count=Count('favorited_by')
        ).filter(favorite_count__gt=0).order_by('-favorite_count')[:10]
        
        popular_attractions_data = [
            {
                'id': attr.id,
                'name': attr.name,
                'favorite_count': attr.favorite_count,
                'rating': float(attr.rating) if attr.rating else 0.0
            }
            for attr in popular_attractions
        ]
        
        return Response({
            'total': total_attractions,
            'with_images': with_images,
            'with_reviews': with_reviews,
            'recent': recent_attractions,
            'category_stats': list(category_stats),
            'popular_attractions': popular_attractions_data
        })
    
    @action(detail=False, methods=['get'])
    def recommendations(self, request):
        """获取推荐景点（使用缓存和性能监控）"""
        start_time = time.time()
        user_id = request.query_params.get('user_id')
        limit = int(request.query_params.get('limit', 10))
        categories = request.query_params.get('categories', '').split(',') if request.query_params.get('categories') else []
        
        # 构建缓存键
        cache_key = f'attraction_recommendations_{user_id or "anonymous"}_{limit}_{"_".join(sorted(categories))}'
        
        # 尝试从缓存获取
        cached_recommendations = cache.get(cache_key)
        if cached_recommendations:
            duration = time.time() - start_time
            performance_monitor.record_api_request(
                endpoint='recommendations',
                method='GET',
                duration=duration,
                status_code=200,
                user_id=int(user_id) if user_id else None
            )
            return Response(cached_recommendations)
        
        try:
            engine = RecommendationEngine()
            recommendations = engine.recommend_attractions(
                user_id=int(user_id) if user_id else None,
                limit=limit,
                categories=[c for c in categories if c]
            )
            
            # 缓存结果
            cache.set(cache_key, recommendations, settings.CACHE_TIMEOUT.get('user_recommendations', 1800))
            
            duration = time.time() - start_time
            performance_monitor.record_api_request(
                endpoint='recommendations',
                method='GET',
                duration=duration,
                status_code=200,
                user_id=int(user_id) if user_id else None
            )
            
            return Response(recommendations)
        except Exception as e:
            duration = time.time() - start_time
            error_collector.record_error(
                error_type=type(e).__name__,
                error_message=str(e),
                traceback_str=traceback.format_exc(),
                context={'endpoint': 'recommendations', 'user_id': user_id}
            )
            raise
    
    @action(detail=True, methods=['post', 'delete'], permission_classes=[IsAuthenticated])
    def favorite(self, request, pk=None):
        """收藏/取消收藏景点"""
        attraction = self.get_object()
        user = request.user
        
        if request.method == 'POST':
            # 收藏景点
            favorite, created = AttractionFavorite.objects.get_or_create(
                user=user,
                attraction=attraction
            )
            if created:
                try:
                    from django.core.cache import cache
                    cache.delete_pattern(f'attraction_recommendations_{user.id}_*')
                    cache.delete_pattern(f'content_based_rec_{user.id}_*')
                except Exception:
                    pass
                return Response({'message': '收藏成功'}, status=status.HTTP_201_CREATED)
            else:
                return Response({'message': '已收藏'}, status=status.HTTP_200_OK)
        
        elif request.method == 'DELETE':
            # 取消收藏
            try:
                favorite = AttractionFavorite.objects.get(user=user, attraction=attraction)
                favorite.delete()
                try:
                    from django.core.cache import cache
                    cache.delete_pattern(f'attraction_recommendations_{user.id}_*')
                    cache.delete_pattern(f'content_based_rec_{user.id}_*')
                except Exception:
                    pass
                return Response({'message': '取消收藏成功'}, status=status.HTTP_200_OK)
            except AttractionFavorite.DoesNotExist:
                return Response(
                    {'error': '未收藏该景点'},
                    status=status.HTTP_404_NOT_FOUND
                )
    
    @action(detail=False, methods=['get'], permission_classes=[IsAuthenticated])
    def favorites(self, request):
        """获取当前用户的收藏列表（分页）"""
        from rest_framework.pagination import PageNumberPagination
        favorites_qs = AttractionFavorite.objects.filter(user=request.user).select_related('attraction').order_by('-created_at')
        attractions_qs = [fav.attraction for fav in favorites_qs]
        paginator = PageNumberPagination()
        try:
            page_size = int(request.query_params.get('page_size', 20))
        except Exception:
            page_size = 20
        paginator.page_size = page_size
        page = paginator.paginate_queryset(attractions_qs, request, view=self)
        serializer = self.get_serializer(page, many=True)
        return paginator.get_paginated_response(serializer.data)
